#pragma once

#include "chan_buffer.h"
#include "evt_loop.h"
#include "fd_chan.h"
#include "socket_util.h"
#include "ssl_util.h"
#include "time_wheel.h"
#include "write_buf.h"

namespace qio {

class TcpConn;

using SharedTcpConn = std::shared_ptr<TcpConn>;
using WeakTcpConn = std::weak_ptr<TcpConn>;

class TcpConn final : public std::enable_shared_from_this<TcpConn> {
 public:
  // 启动
  using OnEstablishedCallback = std::function<void(TcpConn *)>;
  // 断开
  using OnDisconnectedCallback = std::function<void(TcpConn *)>;
  // 读取到数据的回调
  using OnMessageCallback = std::function<void(TcpConn *, Buffer *)>;
  // 错误
  using OnErrorCallback = std::function<void(TcpConn *, int, const char *)>;

  // 回调
  struct Callbacks final {
    friend class TcpConn;

    OnEstablishedCallback establish_cb{nullptr};
    OnDisconnectedCallback disconn_cb{nullptr};
    OnMessageCallback msg_cb{nullptr};
    OnErrorCallback error_cb{nullptr};

   private:
    inline void Reset() {
      establish_cb = nullptr;
      disconn_cb = nullptr;
      msg_cb = nullptr;
      error_cb = nullptr;
    }

    inline void OnEstablished(TcpConn *conn) const {
      if (establish_cb) {
        establish_cb(conn);
      }
    }

    inline void OnDisconnected(TcpConn *conn) const {
      if (disconn_cb) {
        disconn_cb(conn);
      }
    }

    inline void OnMessage(TcpConn *conn, Buffer *buf) const {
      if (msg_cb) {
        msg_cb(conn, buf);
      }
    }

    inline void OnError(TcpConn *conn, int err_code,
                        const char *err_msg) const {
      if (error_cb) {
        error_cb(conn, err_code, err_msg);
      }
    }
  };

  enum SendResult {
    Ok,      // success
    Failed,  // should close socket
    Again    // should put into enqueue or not pop it from queues
  };

  enum Stat { INIT, CONNECTING, CONNECTED, CLOSING, CLOSED };

  enum Mode { SERVER, CLIENT };

  struct Config {
    Fd fd;

    std::uint64_t conn_id;
    IPAddress remote_addr;
    Mode mode;

    char *read_buffer;
    std::size_t read_buffer_len;

    UniqueBuffer read_cache_buffer;  // must not be null

    Callbacks callbacks;
    SharedSSLHelper ssl_helper;
  };

  using UniqueConfig = std::unique_ptr<Config>;

 public:
  ~TcpConn() QIO_NOEXCEPT;

  TcpConn(EventLoop *loop, UniqueConfig &&config) QIO_NOEXCEPT;

  void Send(const SharedWriteBuf &buf) QIO_NOEXCEPT;

  void Send(const std::string &str) QIO_NOEXCEPT;

  void Send(const std::string &str, WriteDoneCallback &&cb) QIO_NOEXCEPT;

  void Send(const char *buf, std::size_t len) QIO_NOEXCEPT;

  void Send(const char *buf, std::size_t len,
            WriteDoneCallback &&cb) QIO_NOEXCEPT;

  void Start() QIO_NOEXCEPT;

  void Close() QIO_NOEXCEPT;

  inline SharedTcpConn ShareInstance() QIO_NOEXCEPT {
    return shared_from_this();
  }

  inline ConnId GetConnId() const QIO_NOEXCEPT { return conn_conf_->conn_id; }

  inline const IPAddress GetRemoteAddress() const QIO_NOEXCEPT {
    return conn_conf_->remote_addr;
  }

  inline Stat GetStat() const QIO_NOEXCEPT { return conn_stat_; }

  inline void SetTimeoutHandle(const SharedTimeoutHandle &hd) QIO_NOEXCEPT {
    timeout_handle_ = hd;
  }

  inline SharedTimeoutHandle GetTimeoutHandle() const QIO_NOEXCEPT {
    return timeout_handle_;
  }

  DISABLE_CPY_MOV(TcpConn)

 private:
  void SendInLoop(SharedWriteBuf buf) QIO_NOEXCEPT;

  void CloseInLoop() QIO_NOEXCEPT;

  void HandleFdEvents(FdChan *, Fd, int) QIO_NOEXCEPT;

  void HandleSSLFdEvents(FdChan *, Fd, int) QIO_NOEXCEPT;

  void HandleRead(FdChan *, Fd) QIO_NOEXCEPT;

  void HandleSSLRead(FdChan *, Fd) QIO_NOEXCEPT;

  void HandleWrite(FdChan *, Fd) QIO_NOEXCEPT;

  void HandleFdClosed(FdChan *) QIO_NOEXCEPT;

  void HandleFdError(FdChan *, int, const char *) QIO_NOEXCEPT;

  SendResult SSLWrite(const SharedWriteBuf &buf) QIO_NOEXCEPT;

  SendResult Write(const SharedWriteBuf &buf) QIO_NOEXCEPT;

  void ConfigSSLContext() QIO_NOEXCEPT;

  inline void ResetTimeoutHandle() QIO_NOEXCEPT {
    if (timeout_handle_) {
      timeout_handle_->Reset();
    }
  }

 private:
  EventLoop *loop_;
  UniqueConfig conn_conf_;
  FdChan *fd_chan_;
  Stat conn_stat_;
  std::list<SharedWriteBuf> write_buf_queue_;
  SSL *ssl_;
  SharedTimeoutHandle timeout_handle_;
};
}  // namespace qio
